/*
 * Copyright 2000 the original author or authors.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "WtTableCmds.h"
#include "WtTable.h"
#include "WtTableUtil.h"
#include "WtUtil.h"
#include "WtBasicCmds.h"

/* Create a custom command that operates on a table */

Tcl_Command WtCreateTableCmd(const char *cmdName,
    Tcl_Obj **tblPtr, Tcl_ObjType *type, Tcl_Interp *interp)
{
  WtTableCmdData *tcd = (WtTableCmdData *)ckalloc(sizeof(WtTableCmdData));
  tcd->tablePtr = tblPtr;
  tcd->type = type;
  return Tcl_CreateObjCommand(interp, cmdName, WtNamedTableCmd,
    (ClientData)tcd, WtDeleteTableCmdProc);
}

void WtDeleteTableCmdProc(ClientData clientData)
{
  if (clientData) {
    ckfree(clientData);
  }
}

/* The custom table command procedure */

int WtNamedTableCmd(ClientData clientData, Tcl_Interp *interp,
    int objc, Tcl_Obj *const objv[])
{
  int ret;
  WtTableCmdData *tcd = (WtTableCmdData *)clientData;
  WtTableMethodHelper(clientData, interp, objc, objv, &ret,
    tcd->tablePtr, tcd->type, 0, 1);
  return ret;
}

/* Get a table variable */

int WtTblCmdGetVar(Tcl_Obj *var, Tcl_Obj **val,
    Tcl_ObjType *type, Tcl_Interp *interp)
{
  int ok = 1;
  *val = Tcl_ObjGetVar2(interp, var, NULL, 0);
  if (*val && !WtConvertToTableObj(*val, type, interp)) {
    ok = 0;
    *val = NULL;
  }
  return ok;
}

/* Get and unshare a table variable */

int WtTblCmdOwnVar(Tcl_Obj *var, Tcl_Obj **val, Tcl_ObjType *type,
    int *created, Tcl_Interp *interp)
{
  int ok = 1;
  Tcl_Obj *obj;

  *val = NULL;
  *created = 0;

  obj = Tcl_ObjGetVar2(interp, var, NULL, 0);

  if (!obj) {
    if (obj = WtNewTableObjWithType(type)) {
      Tcl_IncrRefCount(obj);
      *val = obj;
      *created = 1;
      ok = 1;
    }
  } else if (Tcl_IsShared(obj)) {
    if (obj = Tcl_DuplicateObj(obj)) {
      Tcl_IncrRefCount(obj);
      if (WtConvertToTableObj(obj, type, interp)) {
        *val = obj;
        *created = 1;
        ok = 1;
      } else {
        Tcl_DecrRefCount(obj);
      }
    }
  } else if (WtConvertToTableObj(obj, type, interp)) {
    *val = obj;
    ok = 1;
  }

  return ok;
}

/* Set a table variable */

Tcl_Obj *WtTblCmdSetVar(Tcl_Obj *var, Tcl_Obj *val,
    Tcl_Interp *interp)
{
  Tcl_Obj *ret;
  Tcl_IncrRefCount(val);
  ret = Tcl_ObjSetVar2(interp, var, NULL, val, 0);
  Tcl_DecrRefCount(val);
  return ret;
}

/* Get a table value */

Tcl_Obj *WtTblCmdGetVal(Tcl_Obj *val, Tcl_ObjType *type,
    Tcl_Interp *interp)
{
  return WtConvertToTableObj(val, type, interp) ? val : NULL;
}

/* Helper to manage table objects. */

WtTableCmdStatus WtTableCmdHelper(ClientData clientData, Tcl_Interp *interp,
    int objc, Tcl_Obj *const objv[], int *tclRet, Tcl_ObjType *type,
    int readOnly, int appendUsage)
{
  int status = WT_TBL_CMD_DONE, ret = TCL_ERROR, error = 0;
  int listLen, created = 0, size;
  WtContext *w;
  char *subCmd;
  Tcl_Obj *tbl, *obj;

  w = WtGetAssocContext(interp);

  if (objc < 2) {
    if (appendUsage) {
      WtTableCmdUsageError(interp, objv[0], readOnly);
    }

    *tclRet = TCL_ERROR;

    return WT_TBL_CMD_MISSING;
  }

  subCmd = WtToString(objv[1]);

  if (!strcmp(subCmd, "create")) {

    /* create command */

    if (objc >= 2) {
      tbl = WtNewTableObjWithElements(type, objc - 2, (const Tcl_Obj **)(objv + 2), interp);
      if (tbl) {
        Tcl_SetObjResult(interp, tbl);
        ret = TCL_OK;
      }
    } else if (objc == 3) {
      if (WtConvertToTableObj(objv[2], type, interp)) {
        Tcl_SetObjResult(interp, objv[2]);
        ret = TCL_OK;
      }
    } else {
      Tcl_AppendResult(interp, wtBadUsagePrefix,
        WtToString(objv[0]), " ", WtToString(objv[1]), " ?items?", NULL);
    }

  } else if (!strcmp(subCmd, "count")) {

    /* count command */

    if (objc != 3) {
      Tcl_AppendResult(interp, wtBadUsagePrefix,
        WtToString(objv[0]), " ", WtToString(objv[1]), " table", NULL);
    } else if (tbl = WtTblCmdGetVal(objv[2], type, interp)) {
      size = WtTableSize(tbl);
      Tcl_SetObjResult(interp, Tcl_NewIntObj(size));
      ret = TCL_OK;
    }

  } else if (!strcmp(subCmd, "has")) {

    /* has command */

    if (objc != 4) {
      Tcl_AppendResult(interp, wtBadUsagePrefix,
        WtToString(objv[0]), " ", WtToString(objv[1]), " ",
        " table key", NULL);
    } else if (tbl = WtTblCmdGetVal(objv[2], type, interp)) {
      Tcl_SetObjResult(interp, Tcl_NewBooleanObj(
        WtTableHas(tbl, objv[3])));
      ret = TCL_OK;
    }

  } else if (!strcmp(subCmd, "keys")) {

    /* keys command */

    if (objc != 3) {
      Tcl_AppendResult(interp, wtBadUsagePrefix,
        WtToString(objv[0]), " ", WtToString(objv[1]), " table", NULL);
    } else if (tbl = WtTblCmdGetVal(objv[2], type, interp)) {
      if (WtTableKeys(tbl, &obj, interp)) {
        Tcl_SetObjResult(interp, obj);
        ret = TCL_OK;
      }
    }

  } else if (!strcmp(subCmd, "values")) {

    /* values command */

    if (objc != 3) {
      Tcl_AppendResult(interp, wtBadUsagePrefix,
        WtToString(objv[0]), " ", WtToString(objv[1]), " table", NULL);
    } else if (tbl = WtTblCmdGetVal(objv[2], type, interp)) {
      if (WtTableValues(tbl, &obj, interp)) {
        Tcl_SetObjResult(interp, obj);
        ret = TCL_OK;
      }
    }

  } else if (!strcmp(subCmd, "get")) {

    /* get command */

    if (objc < 4 || objc > 5) {
      Tcl_AppendResult(interp, wtBadUsagePrefix,
        WtToString(objv[0]), " ",
        WtToString(objv[1]), " table key ?defaultValue?", NULL);
    } else if (tbl = WtTblCmdGetVal(objv[2], type, interp)) {
      obj = WtTableGet(tbl, objv[3]);
      if (obj) {
        Tcl_SetObjResult(interp, obj);
        ret = TCL_OK;
      } else if (objc > 4) {
        Tcl_SetObjResult(interp, objv[4]);
        ret = TCL_OK;
      } else {
        Tcl_AppendResult(interp, "No entry with key \"",
          WtToString(objv[3]), "\" found.", NULL);
      }
    }

  } else if (!strcmp(subCmd, "set") && !readOnly) {

    /* set command */

    if (objc != 5) {
      Tcl_AppendResult(interp, wtBadUsagePrefix,
        WtToString(objv[0]), " ", WtToString(objv[1]),
        " table key value", NULL);
    } else if (WtTblCmdOwnVar(objv[2], &tbl, type, &created, interp)) {
      WtTableSet(tbl, objv[3], objv[4]);
      if (WtTblCmdSetVar(objv[2], tbl, interp)) {
        Tcl_SetObjResult(interp, objv[4]);
        ret = TCL_OK;
      }
    }

  } else if (!strcmp(subCmd, "setDefault") && !readOnly) {

    /* setDefault command */

    if (objc != 5) {
      Tcl_AppendResult(interp, wtBadUsagePrefix,
        WtToString(objv[0]), " ", WtToString(objv[1]), " ",
        " table key value", NULL);
    } else if (WtTblCmdGetVar(objv[2], &tbl, type, interp)) {
      if (tbl && (obj = WtTableGet(tbl, objv[3]))) {
        Tcl_SetObjResult(interp, obj);
        ret = TCL_OK;
      } else if (WtTblCmdOwnVar(objv[2], &tbl, type, &created, interp)) {
        WtTableSet(tbl, objv[3], objv[4]);
        if (WtTblCmdSetVar(objv[2], tbl, interp)) {
          Tcl_SetObjResult(interp, objv[4]);
          ret = TCL_OK;
        }
      }
    }

  } else if (!strcmp(subCmd, "lappend") && !readOnly) {

    /* lappend command */

    if (objc != 5) {
      Tcl_AppendResult(interp, wtBadUsagePrefix,
        WtToString(objv[0]), " ", WtToString(objv[1]),
        " table key value", NULL);
    } else if (WtTblCmdOwnVar(objv[2], &tbl, type, &created, interp)) {
      if (WtTableAppendToList(tbl, objv[3], objv[4], &obj, interp)) {
        if (WtTblCmdSetVar(objv[2], tbl, interp)) {
          Tcl_SetObjResult(interp, obj);
          ret = TCL_OK;
        }
      }
    }

  } else if (!strcmp(subCmd, "remove") && !readOnly) {

    /* remove command */

    if (objc != 4) {
      Tcl_AppendResult(interp, wtBadUsagePrefix,
        WtToString(objv[0]), " ", WtToString(objv[1]), " table key", NULL);
    } else if (WtTblCmdOwnVar(objv[2], &tbl, type, &created, interp)) {
      WtTableRemove(tbl, objv[3]);
      if (WtTblCmdSetVar(objv[2], tbl, interp)) {
        ret = TCL_OK;
      }
    }

  } else if (!strcmp(subCmd, "removeList") && !readOnly) {

    /* removeList command */

    if (objc != 4) {
      Tcl_AppendResult(interp, wtBadUsagePrefix,
        WtToString(objv[0]), " ", WtToString(objv[1]), " table list", NULL);
    } else {
      if (Tcl_ListObjLength(interp, objv[3], &listLen) != TCL_OK) {
        Tcl_AppendResult(interp,
          "Value must be a list. Usage: ", WtToString(objv[0]), " ",
          WtToString(objv[1]), " table list", NULL);
      } else if (WtTblCmdOwnVar(objv[2], &tbl, type, &created, interp)) {
        if (WtTableRemoveList(tbl, objv[3], interp)) {
          if (WtTblCmdSetVar(objv[2], tbl, interp)) {
            ret = TCL_OK;
          }
        }
      }
    }

  } else if (!strcmp(subCmd, "items")) {

    /* items command */

    if (objc != 3) {
      Tcl_AppendResult(interp, wtBadUsagePrefix,
        WtToString(objv[0]), " ", WtToString(objv[1]), " table", NULL);
    } else if (tbl = WtTblCmdGetVal(objv[2], type, interp)) {
      Tcl_SetObjResult(interp, tbl);
      ret = TCL_OK;
    }

  } else if (!strcmp(subCmd, "list")) {

    /* list command */

    if (objc != 3) {
      Tcl_AppendResult(interp, wtBadUsagePrefix,
        WtToString(objv[0]), " ", WtToString(objv[1]),
        " table", NULL);
    } else if (tbl = WtTblCmdGetVal(objv[2], type, interp)) {
      if (obj = WtTableToList(tbl, 0, interp)) {
        Tcl_SetObjResult(interp, obj);
        ret = TCL_OK;
      }
    }

  } else if (!strcmp(subCmd, "array")) {

    /* array command */

    if (objc != 4) {
      Tcl_AppendResult(interp, wtBadUsagePrefix,
        WtToString(objv[0]), " ", WtToString(objv[1]),
        " table arrayName", NULL);
    } else if (tbl = WtTblCmdGetVal(objv[2], type, interp)) {
      if (WtTableToArray(interp, tbl, objv[3], 0)) {
        ret = TCL_OK;
      }
    }

  } else if (!strcmp(subCmd, "copy")) {

    /* copy command */

    if (objc != 3) {
      Tcl_AppendResult(interp, wtBadUsagePrefix,
        WtToString(objv[0]), " ", WtToString(objv[1]), " table", NULL);
    } else if (tbl = WtTblCmdGetVal(objv[2], type, interp)) {
      if (obj = Tcl_DuplicateObj(tbl)) {
        Tcl_SetObjResult(interp, obj);
        ret = TCL_OK;
      }
    }

  } else if (!strcmp(subCmd, "clear") && !readOnly) {

    /* clear command */

    if (objc != 3) {
      Tcl_AppendResult(interp, wtBadUsagePrefix,
        WtToString(objv[0]), " ", WtToString(objv[1]), " table", NULL);
    } else if (tbl = WtTblCmdGetVal(objv[2], type, interp)) {
      WtClearTable(tbl);
      ret = TCL_OK;
    }

  } else if (!strcmp(subCmd, "importVars")) {

    /* importVars command */

    if (objc != 3) {
      Tcl_AppendResult(interp, wtBadUsagePrefix,
        WtToString(objv[0]), " ", WtToString(objv[1]), " table", NULL);
    } else if (tbl = WtTblCmdGetVal(objv[2], type, interp)) {
      if (WtImportTableVars(tbl, interp)) {
        ret = TCL_OK;
      }
    }

  } else {

    /* unknown command */

    status = WT_TBL_CMD_NOT_FOUND;

    if (appendUsage) {
      WtTableCmdUsageError(interp, objv[0], readOnly);
    }

  }

  *tclRet = ret;

  if (created && tbl) {
    Tcl_DecrRefCount(tbl);
  }

  return status;
}

void WtTableCmdUsageError(Tcl_Interp *interp, Tcl_Obj *cmd, int readOnly)
{
  Tcl_Obj *interpRes = Tcl_GetObjResult(interp);
  Tcl_AppendResult(interp, wtBadUsagePrefix2, NULL);
  WtTableCmdAppendUsage(interp, cmd, readOnly);
}

void WtTableCmdAppendUsage(Tcl_Interp *interp, Tcl_Obj *cmd, int readOnly)
{
  Tcl_Obj *res = Tcl_GetObjResult(interp);
  char *cmdStr = WtToString(cmd);

  if (readOnly) {
    Tcl_AppendStringsToObj(res,
      cmdStr, " create ?items?\n",
      cmdStr, " count table\n",
      cmdStr, " has table key\n",
      cmdStr, " keys table\n",
      cmdStr, " values table\n",
      cmdStr, " get table key ?defaultValue?\n",
      cmdStr, " items table\n",
      cmdStr, " list table\n",
      cmdStr, " array table arrayName\n",
      cmdStr, " copy table\n",
      NULL);
  } else {
    Tcl_AppendStringsToObj(res,
      cmdStr, " create ?items?\n",
      cmdStr, " count\n",
      cmdStr, " has table key\n",
      cmdStr, " keys table\n",
      cmdStr, " values table\n",
      cmdStr, " get table key ?defaultValue?\n",
      cmdStr, " set table key value\n",
      cmdStr, " setDefault table key value\n",
      cmdStr, " append table key value\n",
      cmdStr, " add table key value\n",
      cmdStr, " remove table key\n",
      cmdStr, " removeList table keys\n",
      cmdStr, " items table\n",
      cmdStr, " list table\n",
      cmdStr, " array table arrayName\n",
      cmdStr, " copy table\n",
      cmdStr, " clear table\n",
      NULL);
  }
}

int WtTableCmd(ClientData clientData, Tcl_Interp *interp,
    int objc, Tcl_Obj *const objv[])
{
  int ret;
  WtTableCmdHelper(clientData, interp, objc, objv, &ret, &WtTableType, 0, 1);
  return ret;
}

int WtCaselessTableCmd(ClientData clientData, Tcl_Interp *interp,
    int objc, Tcl_Obj *const objv[])
{
  int ret;
  WtTableCmdHelper(clientData, interp, objc, objv, &ret,
    &WtCaselessTableType, 0, 1);
  return ret;
}

/* Helper to manage table objects. Implements the "object method args"
   command style. */

WtTableCmdStatus WtTableMethodHelper(ClientData clientData, Tcl_Interp *interp,
    int objc, Tcl_Obj *const objv[], int *tclRet, Tcl_Obj **tblPtr,
    Tcl_ObjType *type, int readOnly, int appendUsage)
{
  int status = WT_TBL_CMD_DONE, ret = TCL_ERROR, error = 0;
  int listLen;
  WtContext *w;
  char *subCmd;
  Tcl_Obj *obj, *tbl;

  w = WtGetAssocContext(interp);

  if (objc < 2) {
    if (appendUsage) {
      WtTableMethodUsageError(interp, objv[0], readOnly);
    }

    *tclRet = TCL_ERROR;

    return WT_TBL_CMD_MISSING;
  }

  subCmd = WtToString(objv[1]);
  tbl = *tblPtr;

  if (!strcmp(subCmd, "count")) {

    /* count command */

    if (objc != 2) {
      Tcl_AppendResult(interp, wtBadUsagePrefix,
        WtToString(objv[0]), " ", WtToString(objv[1]), NULL);
    } else if (WtConvertToTableObj(tbl, type, interp)) {
      Tcl_SetObjResult(interp, Tcl_NewIntObj(WtTableSize(tbl)));
      ret = TCL_OK;
    }

  } else if (!strcmp(subCmd, "has")) {

    /* has command */

    if (objc != 3) {
      Tcl_AppendResult(interp, wtBadUsagePrefix,
        WtToString(objv[0]), " ", WtToString(objv[1]), " ",
        " key", NULL);
    } else if (WtConvertToTableObj(tbl, type, interp)) {
      Tcl_SetObjResult(interp, Tcl_NewBooleanObj(
        WtTableHas(tbl, objv[2])));
      ret = TCL_OK;
    }

  } else if (!strcmp(subCmd, "keys")) {

    /* keys command */

    if (objc != 2) {
      Tcl_AppendResult(interp, wtBadUsagePrefix,
        WtToString(objv[0]), " ", WtToString(objv[1]), NULL);
    } else if (WtConvertToTableObj(tbl, type, interp)) {
      if (WtTableKeys(tbl, &obj, interp)) {
        Tcl_SetObjResult(interp, obj);
        ret = TCL_OK;
      }
    }

  } else if (!strcmp(subCmd, "values")) {

    /* values command */

    if (objc != 2) {
      Tcl_AppendResult(interp, wtBadUsagePrefix,
        WtToString(objv[0]), " ", WtToString(objv[1]), NULL);
    } else if (WtConvertToTableObj(tbl, type, interp)) {
      if (WtTableValues(tbl, &obj, interp)) {
        Tcl_SetObjResult(interp, obj);
        ret = TCL_OK;
      }
    }

  } else if (!strcmp(subCmd, "get")) {

    /* get command */

    if (objc < 3 || objc > 4) {
      Tcl_AppendResult(interp, wtBadUsagePrefix,
        WtToString(objv[0]), " ",
        WtToString(objv[1]), " key ?defaultValue?", NULL);
    } else if (WtConvertToTableObj(tbl, type, interp)) {
      obj = WtTableGet(tbl, objv[2]);
      if (obj) {
        Tcl_SetObjResult(interp, obj);
        ret = TCL_OK;
      } else if (objc > 3) {
        Tcl_SetObjResult(interp, objv[3]);
        ret = TCL_OK;
      } else {
        Tcl_AppendResult(interp, "No entry with key \"",
          WtToString(objv[2]), "\" found.", NULL);
      }
    }

  } else if (!strcmp(subCmd, "set") && !readOnly) {

    /* set command */

    if (objc != 4) {
      Tcl_AppendResult(interp, wtBadUsagePrefix,
        WtToString(objv[0]), " ", WtToString(objv[1]),
        " key value", NULL);
    } else if (WtOwnTableObj(tblPtr, type, interp)) {
      tbl = *tblPtr;
      WtTableSet(tbl, objv[2], objv[3]);
      Tcl_SetObjResult(interp, objv[3]);
      ret = TCL_OK;
    }

  } else if (!strcmp(subCmd, "setDefault") && !readOnly) {

    /* setDefault command */

    if (objc != 4) {
      Tcl_AppendResult(interp, wtBadUsagePrefix,
        WtToString(objv[0]), " ", WtToString(objv[1]), " ",
        " key value", NULL);
    } else if (WtConvertToTableObj(tbl, type, interp)) {
      obj = WtTableGet(tbl, objv[2]);
      if (obj) {
        Tcl_SetObjResult(interp, obj);
        ret = TCL_OK;
      } else if (WtOwnTableObj(tblPtr, type, interp)) {
        tbl = *tblPtr;
        WtTableSet(tbl, objv[2], objv[3]);
        Tcl_SetObjResult(interp, objv[3]);
        ret = TCL_OK;
      }
    }

  } else if (!strcmp(subCmd, "add") && !readOnly) {

    /* add command */

    if (objc != 4) {
      Tcl_AppendResult(interp, wtBadUsagePrefix,
        WtToString(objv[0]), " ", WtToString(objv[1]),
        " key value", NULL);
    } else if (WtOwnTableObj(tblPtr, type, interp)) {
      tbl = *tblPtr;
      if (WtTableAppendToList(tbl, objv[2], objv[3], &obj, interp)) {
        Tcl_SetObjResult(interp, obj);
        ret = TCL_OK;
      }
    }

  } else if (!strcmp(subCmd, "remove") && !readOnly) {

    /* remove command */

    if (objc != 3) {
      Tcl_AppendResult(interp, wtBadUsagePrefix,
        WtToString(objv[0]), " ", WtToString(objv[1]), " key", NULL);
    } else if (WtOwnTableObj(tblPtr, type, interp)) {
      tbl = *tblPtr;
      WtTableRemove(tbl, objv[2]);
      ret = TCL_OK;
    }

  } else if (!strcmp(subCmd, "removeList") && !readOnly) {

    /* removeList command */

    if (objc != 3) {
      Tcl_AppendResult(interp, wtBadUsagePrefix,
        WtToString(objv[0]), " ", WtToString(objv[1]), " list", NULL);
    } else {
      if (Tcl_ListObjLength(interp, objv[2], &listLen) != TCL_OK) {
        Tcl_AppendResult(interp,
          "Value must be a list. Usage: ",
          WtToString(objv[0]), " ", WtToString(objv[1]), " list", NULL);
      } else if (WtOwnTableObj(tblPtr, type, interp)) {
        tbl = *tblPtr;
        if (WtTableRemoveList(tbl, objv[2], interp)) {
          ret = TCL_OK;
        }
      }
    }

  } else if (!strcmp(subCmd, "items")) {

    /* items command */

    if (objc != 2) {
      Tcl_AppendResult(interp, wtBadUsagePrefix,
        WtToString(objv[0]), " ", WtToString(objv[1]), NULL);
    } else if (WtConvertToTableObj(tbl, type, interp)) {
      Tcl_SetObjResult(interp, tbl);
      ret = TCL_OK;
    }

  } else if (!strcmp(subCmd, "list")) {

    /* list command */

    if (objc != 2) {
      Tcl_AppendResult(interp, wtBadUsagePrefix,
        WtToString(objv[0]), " ", WtToString(objv[1]), NULL);
    } else if (WtConvertToTableObj(tbl, type, interp)) {
      if (obj = WtTableToList(tbl, 0, interp)) {
        Tcl_SetObjResult(interp, obj);
        ret = TCL_OK;
      }
    }

  } else if (!strcmp(subCmd, "array")) {

    /* array command */

    if (objc != 3) {
      Tcl_AppendResult(interp, wtBadUsagePrefix,
        WtToString(objv[0]), " ", WtToString(objv[1]), " arrayName",
        NULL);
    } else if (WtConvertToTableObj(tbl, type, interp)) {
      if (WtTableToArray(interp, tbl, objv[2], 0)) {
        ret = TCL_OK;
      }
    }

  } else if (!strcmp(subCmd, "copy")) {

    /* copy command */

    if (WtConvertToTableObj(tbl, type, interp)) {
      Tcl_SetObjResult(interp, Tcl_DuplicateObj(tbl));
      ret = TCL_OK;
    }

  } else if (!strcmp(subCmd, "clear") && !readOnly) {

    /* clear command */

    if (WtOwnTableObj(tblPtr, type, interp)) {
      tbl = *tblPtr;
      WtClearTable(tbl);
      ret = TCL_OK;
    }

  } else if (!strcmp(subCmd, "importVars")) {

    /* importVars command */

    if (objc != 2) {
      Tcl_AppendResult(interp, wtBadUsagePrefix,
        WtToString(objv[0]), " ", WtToString(objv[1]),
        NULL);
    } else if (WtImportTableVars(tbl, interp)) {
      ret = TCL_OK;
    }

  } else {

    /* unknown command */

    status = WT_TBL_CMD_NOT_FOUND;

    if (appendUsage) {
      WtTableMethodUsageError(interp, objv[0], readOnly);
    }

  }

  *tclRet = ret;

  return status;
}

void WtTableMethodUsageError(Tcl_Interp *interp, Tcl_Obj *cmd, int readOnly)
{
  Tcl_Obj *interpRes = Tcl_GetObjResult(interp);
  Tcl_AppendResult(interp, wtBadUsagePrefix2, NULL);
  WtTableMethodAppendUsage(interp, cmd, readOnly);
}

void WtTableMethodAppendUsage(Tcl_Interp *interp, Tcl_Obj *cmd, int readOnly)
{
  Tcl_Obj *res = Tcl_GetObjResult(interp);
  char *cmdStr = WtToString(cmd);

  if (readOnly) {
    Tcl_AppendStringsToObj(res,
      cmdStr, " count\n",
      cmdStr, " has key\n",
      cmdStr, " keys\n",
      cmdStr, " values\n",
      cmdStr, " get key ?defaultValue?\n",
      cmdStr, " items\n",
      cmdStr, " list\n",
      cmdStr, " array arrayName\n",
      cmdStr, " copy\n",
      cmdStr, " importVars\n",
      NULL);
  } else {
    Tcl_AppendStringsToObj(res,
      cmdStr, " count\n",
      cmdStr, " has key\n",
      cmdStr, " keys\n",
      cmdStr, " values\n",
      cmdStr, " get key ?defaultValue?\n",
      cmdStr, " set key value\n",
      cmdStr, " setDefault key value\n",
      cmdStr, " append key value\n",
      cmdStr, " add key value\n",
      cmdStr, " remove key\n",
      cmdStr, " removeList keys\n",
      cmdStr, " items\n",
      cmdStr, " list\n",
      cmdStr, " array arrayName\n",
      cmdStr, " copy\n",
      cmdStr, " clear\n",
      cmdStr, " importVars\n",
      NULL);
  }
}

void WtInitTableCommands(Tcl_Interp *interp)
{
  Tcl_CreateObjCommand(interp, "table", WtTableCmd, NULL, NULL);
  Tcl_CreateObjCommand(interp, "itable", WtCaselessTableCmd, NULL, NULL);
}
